/**
 * 1、Promise 是一个类 在执行类的时候需要传递一个函数 函数会立即执行
 * 2、Promise 中有三种状态 分别是 成功 fulfilled 失败 rejected 等待 pending
 *    状态可以由 pending -> fulfilled
 *              pending -> rejected
 *    一旦状态确定就不可更改
 * 3、resolve 和 reject 函数是用来更改状态的
 *    resolve -> fulfilled
 *    reject -> rejected
 * 4、then 方法内部做的事情就是判断状态 如果状态是成功就调用成功的回调函数，如果状态是失败，就调用失败的回调函数   then 方法是定义在原型对象中的方法
 * 5、then 成功回调有一个参数 表示成功之后的值 then  失败回调有一个参数，表示失败的原因
 */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class writePromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error)
    }
  }
  // 状态
  status = PENDING;
  // 成功之后的值
  value = undefined;
  // 失败的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];
  // 定义为箭头函数是为了让函数内部的 this 指向这个类的实例对象 指向promise对象
  resolve = value => {
    if (this.status !== PENDING) return;
    // 更改状态
    this.status = FULFILLED;
    this.value = value;
    // 判断回调是否存在
    // this.successCallback && this.successCallback(this.value);
    while (this.successCallback.length) {
      this.successCallback.shift()()
    }
  }
  reject = reason => {
    if (this.status !== PENDING) return;
    // 更改状态
    this.status = REJECTED;
    this.reason = reason;
    // 判断回调是否存在
    // this.failCallback && this.failCallback(this.reason);
    while (this.failCallback.length) {
      this.failCallback.shift()()
    }
  }
  then(successCallback, failCallback) {
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => {throw reason};
    let promise2 = new writePromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(()=>{
          try {
            let x = successCallback(this.value)
            // 判断 x 值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promise对象的返回结果
            // 再根据promise对象返回的结果 决定调用 resolve 还是调用reject
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        },0)
      } else if (this.status === REJECTED) {
        setTimeout(()=>{
          try {
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      } else {
        // 等待
        // 将成功回调和失败回调存起来
        this.successCallback.push(()=>{
          setTimeout(()=>{
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        });
        this.failCallback.push(()=>{
          setTimeout(()=>{
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        });
      }
    });
    return promise2;
  }
  finally(callback){
    return this.then(value=>{
      return writePromise.resolve(callback()).then(()=>value)
    },reason=>{
      return writePromise.resolve(callback()).then(()=>{throw reason})
    })
  }
  catch(failCallback){
    return this.then(undefined, failCallback)
  }
  static all(array) {
    let result = [];
    let index = 0;
    return new writePromise((resolve,reject)=>{
      function addData(key,value){
        result[key] = value;
        index++;
        if(index === array.length){
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if(current instanceof writePromise){
          // promise对象
          current.then(value=>{
            addData(i,value)
          },reason=>{
            reject(reason)
          })
        }else{
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }
  static resolve(value){
    if(value instanceof writePromise)return value;
    return new writePromise(resolve => resolve(value))
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x){
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof writePromise) {
    // promise对象
    // x.then(value => {
    //   resolve(value)
    // }, reason => {
    //   reject(reason)
    // })
    x.then(resolve,reject)
  } else {
    // 普通值
    resolve(x)
  }
}
module.exports = writePromise